package com.gitee.feizns.menu.intercetor;

import com.gitee.feizns.menu.Menu;
import com.gitee.feizns.menu.annotation.MenuVerificationProp;
import com.gitee.feizns.menu.exception.NoValidationAttributeException;
import com.gitee.feizns.menu.exception.UnauthorizedException;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;

/**
 * @author feizns
 * @since 2019/5/4 0004
 */
public class MenuEnumVerificationInterceptor<A extends Annotation> implements HandlerInterceptor {

    /**
     * 自定义注解类型.
     */
    private Class<A> annotation;

    /**
     * 获取需要验证的自定义注解属性名称
     */
    private String propName;

    /**
     * 权限验证处理过程
     */
    private VerificationHandler[] handlers;

    public MenuEnumVerificationInterceptor(Class<A> annotation, VerificationHandler... handler) {

        this.annotation = annotation;

        MenuVerificationProp prop = annotation.getAnnotation(MenuVerificationProp.class);
        if ( prop == null )
            throw new NoValidationAttributeException(annotation + " no validation properties declared");

        this.propName = getPropName(prop);

        this.handlers = handler;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if ( handler instanceof HandlerMethod ) {
            HandlerMethod method = (HandlerMethod) handler;

            A annotation = getAnnotation(method);

            if ( annotation != null ) {
                Menu menu = getAttribute(annotation);

                if ( this.handlers != null ) {
                    for (VerificationHandler verificationHandler : this.handlers) {
                        if ( verificationHandler.verify(menu) == false )
                            throw new UnauthorizedException("Permission denied: " + menu, menu);
                    }
                }
            }

        }
        return true;
    }

    /**
     * 获取属性
     * @param prop
     * @return
     */
    private String getPropName(MenuVerificationProp prop) {
        String name = prop.prop();
        return name != null ? name : prop.value();
    }

    /**
     *
     * @param method
     * @return
     */
    private A getAnnotation(HandlerMethod method) {
        A methodAnnotation = method.getMethodAnnotation(annotation);
        return methodAnnotation != null
                ? methodAnnotation
                : method.getBeanType().getAnnotation(annotation);
    }

    /**
     *
     * @param annotation
     * @return
     */
    private Menu getAttribute(A annotation) {
        try {
            Object result = annotation.getClass().getMethod(this.propName).invoke(annotation);

            if ( result == null )
                throw new RuntimeException(annotation + "中不存在属性：" + propName);

            if ( Menu.class.isAssignableFrom(result.getClass()) == false )
                throw new RuntimeException(annotation + " 属性: "+ propName + " 必须实现:" + Menu.class.getName());

            return (Menu) result;
        } catch (ReflectiveOperationException e) {
            throw new RuntimeException(e);
        }
    }

}
